/***********************************************************************************
# File Name:    led_control.c
# Author:       ciyeer
# Mail:         ciyeer@outlook.com 
# Created Time: 2025年11月10日 星期一 18时55分32秒
 **********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <termios.h>

#define JSON_RESPONSE "Content-Type: application/json;charset=UTF-8\r\n\r\n"
#define SERIAL_PORT "/dev/ttyUSB0"
#define LOCK_FILE "/tmp/zigbee_lock"

int init_serial();
int acquire_lock();
int release_lock();
int send_command(const char *cmd);
void urldecode(char *dst, const char *src);

int main() {
    printf(JSON_RESPONSE);
    
    char *query_string = getenv("QUERY_STRING");
    if (!query_string) {
        printf("{\"status\":\"error\",\"msg\":\"缺少参数\"}");
        return 1;
    }
    
    char action[32] = {0};
    char value[32] = {0};
    
    char *token = strtok(query_string, "&");
    while (token) {
        char *eq = strchr(token, '=');
        if (eq) {
            *eq = '\0';
            char *name = token;
            char *val = eq + 1;
            urldecode(val, val);
            
            if (strcmp(name, "action") == 0) strncpy(action, val, 31);
            else if (strcmp(name, "value") == 0) strncpy(value, val, 31);
        }
        token = strtok(NULL, "&");
    }
    
    if (strlen(action) == 0) {
        printf("{\"status\":\"error\",\"msg\":\"缺少action参数\"}");
        return 1;
    }
    
    if (strcmp(action, "on") == 0) {
        char cmd[128];
        snprintf(cmd, sizeof(cmd), "{\"device_type\":\"led\",\"action\":\"on\"}");
        if (send_command(cmd) == 0)
            printf("{\"status\":\"success\",\"msg\":\"LED已开启\"}");
        else
            printf("{\"status\":\"error\",\"msg\":\"LED开启失败\"}");
    }
    else if (strcmp(action, "off") == 0) {
        char cmd[128];
        snprintf(cmd, sizeof(cmd), "{\"device_type\":\"led\",\"action\":\"off\"}");
        if (send_command(cmd) == 0)
            printf("{\"status\":\"success\",\"msg\":\"LED已关闭\"}");
        else
            printf("{\"status\":\"error\",\"msg\":\"LED关闭失败\"}");
    }
    else if (strcmp(action, "brightness") == 0 && strlen(value) > 0) {
        char cmd[128];
        snprintf(cmd, sizeof(cmd), "{\"device_type\":\"led\",\"action\":\"brightness\",\"value\":%d}", atoi(value));
        if (send_command(cmd) == 0)
            printf("{\"status\":\"success\",\"msg\":\"亮度设置成功\",\"value\":%s}", value);
        else
            printf("{\"status\":\"error\",\"msg\":\"亮度设置失败\"}");
    }
    else {
        printf("{\"status\":\"error\",\"msg\":\"不支持的LED操作\"}");
    }
    
    return 0;
}

int init_serial() {
    int fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) return -1;
    
    struct termios tty;
    if (tcgetattr(fd, &tty) != 0) {
        close(fd);
        return -1;
    }
    
    cfsetospeed(&tty, B9600);
    cfsetispeed(&tty, B9600);
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 5;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;
    
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        close(fd);
        return -1;
    }
    
    return fd;
}

int acquire_lock() {
    int lock_fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0666);
    if (lock_fd < 0) return -1;
    if (flock(lock_fd, LOCK_EX | LOCK_NB) != 0) {
        close(lock_fd);
        return -1;
    }
    return lock_fd;
}

int release_lock() {
    int lock_fd = open(LOCK_FILE, O_RDWR);
    if (lock_fd < 0) return -1;
    flock(lock_fd, LOCK_UN);
    close(lock_fd);
    return 0;
}

int send_command(const char *cmd) {
    int lock_fd = acquire_lock();
    if (lock_fd < 0) return -1;
    
    int serial_fd = init_serial();
    if (serial_fd < 0) {
        release_lock();
        return -1;
    }
    
    char full_cmd[256];
    snprintf(full_cmd, sizeof(full_cmd), "%s\n", cmd);
    write(serial_fd, full_cmd, strlen(full_cmd));
    tcdrain(serial_fd);
    
    close(serial_fd);
    release_lock();
    return 0;
}

void urldecode(char *dst, const char *src) {
    char a, b;
    while (*src) {
        if ((*src == '%') && (a = src[1]) && (b = src[2]) && isxdigit(a) && isxdigit(b)) {
            if (a >= 'a') a -= 'a'-'A';
            if (a >= 'A') a -= ('A' - 10);
            else a -= '0';
            if (b >= 'a') b -= 'a'-'A';
            if (b >= 'A') b -= ('A' - 10);
            else b -= '0';
            *dst++ = 16*a+b;
            src += 3;
        } else if (*src == '+') {
            *dst++ = ' ';
            src++;
        } else {
            *dst++ = *src++;
        }
    }
    *dst = '\0';
}
